package android.car;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.Socket;
import java.net.UnknownHostException;

import javax.microedition.khronos.opengles.GL10;

import org.anddev.andengine.engine.Engine;
import org.anddev.andengine.engine.camera.Camera;
import org.anddev.andengine.engine.camera.hud.controls.BaseOnScreenControl;
import org.anddev.andengine.engine.camera.hud.controls.DigitalOnScreenControl;
import org.anddev.andengine.engine.camera.hud.controls.BaseOnScreenControl.IOnScreenControlListener;
import org.anddev.andengine.engine.options.EngineOptions;
import org.anddev.andengine.engine.options.EngineOptions.ScreenOrientation;
import org.anddev.andengine.engine.options.resolutionpolicy.RatioResolutionPolicy;
import org.anddev.andengine.entity.scene.Scene;
import org.anddev.andengine.entity.scene.Scene.IOnAreaTouchListener;
import org.anddev.andengine.entity.scene.Scene.IOnSceneTouchListener;
import org.anddev.andengine.entity.scene.Scene.ITouchArea;
import org.anddev.andengine.entity.scene.background.ColorBackground;
import org.anddev.andengine.entity.sprite.AnimatedSprite;
import org.anddev.andengine.entity.sprite.Sprite;
import org.anddev.andengine.entity.util.FPSLogger;
import org.anddev.andengine.examples.adt.messages.client.ClientMessageFlags;
import org.anddev.andengine.examples.adt.messages.server.ConnectionCloseServerMessage;
import org.anddev.andengine.examples.adt.messages.server.ServerMessageFlags;
import org.anddev.andengine.extension.multiplayer.protocol.adt.message.IMessage;
import org.anddev.andengine.extension.multiplayer.protocol.adt.message.server.IServerMessage;
import org.anddev.andengine.extension.multiplayer.protocol.adt.message.server.ServerMessage;
import org.anddev.andengine.extension.multiplayer.protocol.client.IServerMessageHandler;
import org.anddev.andengine.extension.multiplayer.protocol.client.connector.ServerConnector;
import org.anddev.andengine.extension.multiplayer.protocol.client.connector.SocketConnectionServerConnector;
import org.anddev.andengine.extension.multiplayer.protocol.client.connector.SocketConnectionServerConnector.ISocketConnectionServerConnectorListener;
import org.anddev.andengine.extension.multiplayer.protocol.server.SocketServer;
import org.anddev.andengine.extension.multiplayer.protocol.server.SocketServer.ISocketServerListener;
import org.anddev.andengine.extension.multiplayer.protocol.server.connector.ClientConnector;
import org.anddev.andengine.extension.multiplayer.protocol.server.connector.SocketConnectionClientConnector;
import org.anddev.andengine.extension.multiplayer.protocol.server.connector.SocketConnectionClientConnector.ISocketConnectionClientConnectorListener;
import org.anddev.andengine.extension.multiplayer.protocol.shared.SocketConnection;
import org.anddev.andengine.extension.multiplayer.protocol.util.MessagePool;
import org.anddev.andengine.extension.multiplayer.protocol.util.WifiUtils;
import org.anddev.andengine.input.touch.TouchEvent;
import org.anddev.andengine.opengl.texture.Texture;
import org.anddev.andengine.opengl.texture.TextureOptions;
import org.anddev.andengine.opengl.texture.atlas.bitmap.BitmapTextureAtlas;
import org.anddev.andengine.opengl.texture.atlas.bitmap.BitmapTextureAtlasTextureRegionFactory;
import org.anddev.andengine.opengl.texture.region.TextureRegion;
import org.anddev.andengine.opengl.texture.region.TiledTextureRegion;
import org.anddev.andengine.util.Debug;
import android.app.AlertDialog;
import android.car.R.string;
import android.content.DialogInterface;
import android.content.DialogInterface.OnClickListener;
import android.os.Bundle;
import android.util.SparseArray;
import android.view.KeyEvent;
import android.widget.EditText;
import android.widget.Toast;

/**
 * @author Nicolas Gramlich
 * @since 17:10:24 - 19.06.2010
 */
public class Car extends BaseExample implements ClientMessageFlags,
		ServerMessageFlags {
	// ===========================================================
	// Constants
	// ===========================================================

	private static final String LOCALHOST_IP = "127.0.0.1";

	private static final int CAMERA_WIDTH = 720;
	private static final int CAMERA_HEIGHT = 480;

	private static final int SERVER_PORT = 4444;

	private static final short FLAG_MESSAGE_SERVER_ADD_FACE = 1;
	private static final short FLAG_MESSAGE_SERVER_MOVE_FACE = FLAG_MESSAGE_SERVER_ADD_FACE + 1;
	private static final short FLAG_MESSAGE_SERVER_MOVE_CAR = FLAG_MESSAGE_SERVER_MOVE_FACE + 1;

	private static final int DIALOG_CHOOSE_SERVER_OR_CLIENT_ID = 0;
	private static final int DIALOG_ENTER_SERVER_IP_ID = DIALOG_CHOOSE_SERVER_OR_CLIENT_ID + 1;
	private static final int DIALOG_SHOW_SERVER_IP_ID = DIALOG_ENTER_SERVER_IP_ID + 1;

	// ===========================================================
	// Fields
	// ===========================================================

	private Camera mCamera;

	private BitmapTextureAtlas mBitmapTextureAtlas;
	private TextureRegion mFaceTextureRegion;

	private BitmapTextureAtlas mNumbersBitmapTextureAtlas;
	private TiledTextureRegion mNumbersTiledTextureRegion;
	private AnimatedSprite mNumbersAnimatedSprite;

	private BitmapTextureAtlas mOnScreenControlTexture;
	private TextureRegion mOnScreenControlBaseTextureRegion;
	private TextureRegion mOnScreenControlKnobTextureRegion;

	private DigitalOnScreenControl mDigitalOnScreenControl;

	private int mFaceIDCounter;
	private final SparseArray<Sprite> mFaces = new SparseArray<Sprite>();

	private String mServerIP = LOCALHOST_IP;
	private SocketServer<SocketConnectionClientConnector> mSocketServer;
	private ServerConnector<SocketConnection> mServerConnector;

	private final MessagePool<IMessage> mMessagePool = new MessagePool<IMessage>();

	private String device = null;

	private static final int CLIENT = 0;
	private static final int SERVER = CLIENT + 1;
	private int CONNECT_TYPE = SERVER;

	// ===========================================================
	// Constructors
	// ===========================================================

	public Car() {
		this.initMessagePool();
	}

	private void initMessagePool() {
		this.mMessagePool.registerMessage(FLAG_MESSAGE_SERVER_ADD_FACE,
				AddFaceServerMessage.class);
		this.mMessagePool.registerMessage(FLAG_MESSAGE_SERVER_MOVE_FACE,
				MoveFaceServerMessage.class);
		this.mMessagePool.registerMessage(FLAG_MESSAGE_SERVER_MOVE_CAR,
				MoveCarServerMessage.class);
	}

	// ===========================================================
	// Getter & Setter
	// ===========================================================

	// ===========================================================
	// Methods for/from SuperClass/Interfaces
	// ===========================================================

	@Override
	public Engine onLoadEngine() {
		Bundle bundle = getIntent().getExtras();

		device = bundle.getString("Be");

		if (device.equals("Car")) {
			Car.this.initServer();
			try {
				CreateCarDialog().show();
			} catch (UnknownHostException e) {
				e.printStackTrace();
			}
		} else if (device.equals("Phone")) {
			CreatePhoneDialog().show();
		}

		this.mCamera = new Camera(0, 0, CAMERA_WIDTH, CAMERA_HEIGHT);

		return new Engine(new EngineOptions(true, ScreenOrientation.LANDSCAPE,
				new RatioResolutionPolicy(CAMERA_WIDTH, CAMERA_HEIGHT),
				this.mCamera));
	}

	@Override
	public void onLoadResources() {
		BitmapTextureAtlasTextureRegionFactory.setAssetBasePath("gfx/");
		
		this.onLoadResourcesFace();
		this.onLoadResourcesNumbers();
		this.onLoadResourcesControl();
		
		this.mEngine.getTextureManager().loadTextures(this.mBitmapTextureAtlas,
				this.mNumbersBitmapTextureAtlas, this.mOnScreenControlTexture);
	}

	@Override
	public Scene onLoadScene() {
		this.mEngine.registerUpdateHandler(new FPSLogger());

		final Scene scene = new Scene();

		scene.setBackground(new ColorBackground(0.09804f, 0.6274f, 0.8784f));

		this.onLoadSceneNumbers(scene);
		this.onLoadSceneControl(scene);
		
		/* We allow only the server to actively send around messages. */
		if (Car.this.mSocketServer != null) {
			scene.setOnSceneTouchListener(new IOnSceneTouchListener() {
				@Override
				public boolean onSceneTouchEvent(final Scene pScene,
						final TouchEvent pSceneTouchEvent) {
					if (pSceneTouchEvent.isActionDown()) {
						try {
							final AddFaceServerMessage addFaceServerMessage = (AddFaceServerMessage) Car.this.mMessagePool
									.obtainMessage(FLAG_MESSAGE_SERVER_ADD_FACE);
							addFaceServerMessage.set(Car.this.mFaceIDCounter++,
									pSceneTouchEvent.getX(),
									pSceneTouchEvent.getY());

							Car.this.mSocketServer
									.sendBroadcastServerMessage(addFaceServerMessage);

							Car.this.mMessagePool
									.recycleMessage(addFaceServerMessage);
						} catch (final IOException e) {
							Debug.e(e);
						}
						return true;
					} else {
						return true;
					}
				}
			});

			scene.setOnAreaTouchListener(new IOnAreaTouchListener() {
				@Override
				public boolean onAreaTouched(final TouchEvent pSceneTouchEvent,
						final ITouchArea pTouchArea,
						final float pTouchAreaLocalX,
						final float pTouchAreaLocalY) {
					try {
						final Sprite face = (Sprite) pTouchArea;
						final Integer faceID = (Integer) face.getUserData();
						final MoveFaceServerMessage moveFaceServerMessage = (MoveFaceServerMessage) Car.this.mMessagePool
								.obtainMessage(FLAG_MESSAGE_SERVER_MOVE_FACE);
						
						moveFaceServerMessage.set(faceID,
								pSceneTouchEvent.getX(),
								pSceneTouchEvent.getY());

						Car.this.mSocketServer
								.sendBroadcastServerMessage(moveFaceServerMessage);

						Car.this.mMessagePool
								.recycleMessage(moveFaceServerMessage);
					} catch (final IOException e) {
						Debug.e(e);
						return false;
					}
					return true;
				}
			});

			scene.setTouchAreaBindingEnabled(true);
		}
		
		return scene;
	}

	@Override
	public void onLoadComplete() {

	}

	@Override
	protected void onDestroy() {
		if (this.mSocketServer != null) {
			try {
				this.mSocketServer
						.sendBroadcastServerMessage(new ConnectionCloseServerMessage());
			} catch (final IOException e) {
				Debug.e(e);
			}
			this.mSocketServer.terminate();
		}

		if (this.mServerConnector != null) {
			this.mServerConnector.terminate();
		}

		super.onDestroy();
	}

	@Override
	public boolean onKeyUp(final int pKeyCode, final KeyEvent pEvent) {
		switch (pKeyCode) {
		case KeyEvent.KEYCODE_BACK:
			this.finish();
			return true;
		}
		return super.onKeyUp(pKeyCode, pEvent);
	}

	// ===========================================================
	// Methods
	// ===========================================================

	public void addFace(final int pID, final float pX, final float pY) {
		final Scene scene = this.mEngine.getScene();
		/* Create the face and add it to the scene. */
		final Sprite face = new Sprite(0, 0, this.mFaceTextureRegion);
		face.setPosition(pX - face.getWidth() * 0.5f, pY - face.getHeight()
				* 0.5f);
		face.setUserData(pID);
		this.mFaces.put(pID, face);
		scene.registerTouchArea(face);
		scene.attachChild(face);
	}

	public void moveFace(final int pID, final float pX, final float pY) {
		/* Find and move the face. */
		final Sprite face = this.mFaces.get(pID);
		face.setPosition(pX - face.getWidth() * 0.5f, pY - face.getHeight()
				* 0.5f);
	}

	private void showDirection(int direction) {
		 this.mNumbersAnimatedSprite.setCurrentTileIndex(direction);
	}

	// ===========================================================
	// Transfer package Methods
	// ===========================================================

	private void initServerAndClient() {
		Car.this.initServer();

		/*
		 * Wait some time after the server has been started, so it actually can
		 * start up.
		 */
		try {
			Thread.sleep(500);
		} catch (final Throwable t) {
			Debug.e(t);
		}

		Car.this.initClient();
	}

	private void initServer() {
		this.mSocketServer = new SocketServer<SocketConnectionClientConnector>(
				SERVER_PORT, new ExampleClientConnectorListener(),
				new ExampleServerStateListener()) {
			@Override
			protected SocketConnectionClientConnector newClientConnector(
					final SocketConnection pSocketConnection)
					throws IOException {
				return new SocketConnectionClientConnector(pSocketConnection);
			}
		};

		this.mSocketServer.start();
	}

	private void initClient() {
		try {
			this.mServerConnector = new SocketConnectionServerConnector(
					new SocketConnection(
							new Socket(this.mServerIP, SERVER_PORT)),
					new ExampleServerConnectorListener());

			this.mServerConnector.registerServerMessage(
					FLAG_MESSAGE_SERVER_CONNECTION_CLOSE,
					ConnectionCloseServerMessage.class,
					new IServerMessageHandler<SocketConnection>() {
						@Override
						public void onHandleMessage(
								final ServerConnector<SocketConnection> pServerConnector,
								final IServerMessage pServerMessage)
								throws IOException {
							Car.this.finish();
						}
					});

			this.mServerConnector.registerServerMessage(
					FLAG_MESSAGE_SERVER_ADD_FACE, AddFaceServerMessage.class,
					new IServerMessageHandler<SocketConnection>() {
						@Override
						public void onHandleMessage(
								final ServerConnector<SocketConnection> pServerConnector,
								final IServerMessage pServerMessage)
								throws IOException {
							final AddFaceServerMessage addFaceServerMessage = (AddFaceServerMessage) pServerMessage;
							Car.this.addFace(addFaceServerMessage.mID,
									addFaceServerMessage.mX,
									addFaceServerMessage.mY);
						}
					});

			this.mServerConnector.registerServerMessage(
					FLAG_MESSAGE_SERVER_MOVE_FACE, MoveFaceServerMessage.class,
					new IServerMessageHandler<SocketConnection>() {
						@Override
						public void onHandleMessage(
								final ServerConnector<SocketConnection> pServerConnector,
								final IServerMessage pServerMessage)
								throws IOException {
							final MoveFaceServerMessage moveFaceServerMessage = (MoveFaceServerMessage) pServerMessage;
							Car.this.moveFace(moveFaceServerMessage.mID,
									moveFaceServerMessage.mX,
									moveFaceServerMessage.mY);
						}
					});

			this.mServerConnector.registerServerMessage(
					FLAG_MESSAGE_SERVER_MOVE_CAR, MoveCarServerMessage.class,
					new IServerMessageHandler<SocketConnection>() {
						@Override
						public void onHandleMessage(
								ServerConnector<SocketConnection> pServerConnector,
								IServerMessage pServerMessage)
								throws IOException {
							final MoveCarServerMessage moveCarServerMessage = (MoveCarServerMessage) pServerMessage;
							Car.this.showDirection(moveCarServerMessage.mDirection);
						}

					});

			this.mServerConnector.getConnection().start();
		} catch (final Throwable t) {
			Debug.e(t);
		}
	}

	// ===========================================================
	// Create Dialog Methods
	// ===========================================================

	private AlertDialog CreateCarDialog() throws UnknownHostException {

		return new AlertDialog.Builder(this)
				.setIcon(android.R.drawable.ic_dialog_info)
				.setTitle("Your Server-IP ...")
				.setCancelable(false)
				//.setMessage(
				//		"The IP of your Server is:\n"
				//				+ WifiUtils.getWifiIPv4Address(Car.this))
				.setPositiveButton(android.R.string.ok, null).create();
	}

	private AlertDialog CreatePhoneDialog() {
		final EditText ipEditText = new EditText(Car.this);

		return new AlertDialog.Builder(this)
				.setIcon(android.R.drawable.ic_dialog_info)
				.setTitle("Enter Server-IP ...")
				.setCancelable(false)
				.setView(ipEditText)
				.setPositiveButton("Connect", new OnClickListener() {
					@Override
					public void onClick(final DialogInterface pDialog,
							final int pWhich) {
						Car.this.CONNECT_TYPE = CLIENT;
						Car.this.mServerIP = ipEditText.getText().toString();
						Car.this.initServerAndClient();
					}
				})
				.setNegativeButton(android.R.string.cancel,
						new OnClickListener() {
							@Override
							public void onClick(final DialogInterface pDialog,
									final int pWhich) {
								Car.this.finish();
							}
						}).create();
	}

	// ===========================================================
	// Load Resource Methods
	// ===========================================================

	private void onLoadResourcesFace() {
		this.mBitmapTextureAtlas = new BitmapTextureAtlas(32, 32,
				TextureOptions.BILINEAR_PREMULTIPLYALPHA);
		this.mFaceTextureRegion = BitmapTextureAtlasTextureRegionFactory
				.createFromAsset(this.mBitmapTextureAtlas, this,
						"face_box.png", 0, 0);
	}
	
	private void onLoadResourcesNumbers() {
		this.mNumbersBitmapTextureAtlas = new BitmapTextureAtlas(512, 32,
				TextureOptions.BILINEAR_PREMULTIPLYALPHA);
		this.mNumbersTiledTextureRegion = BitmapTextureAtlasTextureRegionFactory
				.createTiledFromAsset(this.mNumbersBitmapTextureAtlas, this,
						"numbers.png", 0, 0, 10, 1);
		this.mNumbersAnimatedSprite = new AnimatedSprite(0, 0,
				this.mNumbersTiledTextureRegion);
	}
	
	private void onLoadResourcesControl() {
		this.mOnScreenControlTexture = new BitmapTextureAtlas(256, 128,
				TextureOptions.BILINEAR_PREMULTIPLYALPHA);
		this.mOnScreenControlBaseTextureRegion = BitmapTextureAtlasTextureRegionFactory
				.createFromAsset(this.mOnScreenControlTexture, this,
						"onscreen_control_base.png", 0, 0);
		this.mOnScreenControlKnobTextureRegion = BitmapTextureAtlasTextureRegionFactory
				.createFromAsset(this.mOnScreenControlTexture, this,
						"onscreen_control_knob.png", 128, 0);
	}
	
	// ===========================================================
	// Load Scene Methods
	// ===========================================================

	private void onLoadSceneNumbers(Scene scene) {
		this.mNumbersAnimatedSprite.setScale(3);
		this.mNumbersAnimatedSprite.setPosition(400, 100);
		this.mNumbersAnimatedSprite.setCurrentTileIndex(0);
		scene.attachChild(mNumbersAnimatedSprite);
	}
	
	private void onLoadSceneControl(Scene scene) {
		this.mDigitalOnScreenControl = new DigitalOnScreenControl(0,
				CAMERA_HEIGHT
						- this.mOnScreenControlBaseTextureRegion.getHeight(),
				this.mCamera, this.mOnScreenControlBaseTextureRegion,
				this.mOnScreenControlKnobTextureRegion, 0.1f,
				new IOnScreenControlListener() {
					@Override
					public void onControlChange(
							final BaseOnScreenControl pBaseOnScreenControl,
							final float pValueX, final float pValueY) {
						// physicsHandler.setVelocity(pValueX * 100, pValueY *
						// 100);
						try {
							final MoveCarServerMessage moveCarServerMessage = (MoveCarServerMessage) Car.this.mMessagePool
									.obtainMessage(FLAG_MESSAGE_SERVER_MOVE_CAR);

							if((int)pValueY == -1) // front
								moveCarServerMessage.set(1);
							else if((int)pValueY == 1) // back
								moveCarServerMessage.set(2);
							else if((int)pValueX == -1) // left
								moveCarServerMessage.set(3);
							else if((int)pValueX == 1) // right
								moveCarServerMessage.set(4);

							Car.this.mSocketServer
									.sendBroadcastServerMessage(moveCarServerMessage);
							Car.this.mMessagePool
									.recycleMessage(moveCarServerMessage);
						} catch (IOException e) {
							e.printStackTrace();
						}
					}
				});
		this.mDigitalOnScreenControl.getControlBase().setBlendFunction(
				GL10.GL_SRC_ALPHA, GL10.GL_ONE_MINUS_SRC_ALPHA);
		this.mDigitalOnScreenControl.getControlBase().setAlpha(0.5f);
		this.mDigitalOnScreenControl.getControlBase().setScaleCenter(0, 128);
		this.mDigitalOnScreenControl.getControlBase().setScale(1.25f);
		this.mDigitalOnScreenControl.getControlKnob().setScale(1.25f);
		this.mDigitalOnScreenControl.refreshControlKnobPosition();
		//this.mDigitalOnScreenControl.setAllowDiagonal(true);
		scene.setChildScene(this.mDigitalOnScreenControl);
	}
	
	// ===========================================================
	// Hint Methods
	// ===========================================================

	private void log(final String pMessage) {
		Debug.d(pMessage);
	}

	private void toast(final String pMessage) {
		this.log(pMessage);
		this.runOnUiThread(new Runnable() {
			@Override
			public void run() {
				Toast.makeText(Car.this, pMessage, Toast.LENGTH_SHORT).show();
			}
		});
	}

	// ===========================================================
	// Inner and Anonymous Classes
	// ===========================================================

	public static class AddFaceServerMessage extends ServerMessage {
		private int mID;
		private float mX;
		private float mY;

		public AddFaceServerMessage() {

		}

		public AddFaceServerMessage(final int pID, final float pX,
				final float pY) {
			this.mID = pID;
			this.mX = pX;
			this.mY = pY;
		}

		public void set(final int pID, final float pX, final float pY) {
			this.mID = pID;
			this.mX = pX;
			this.mY = pY;
		}

		@Override
		public short getFlag() {
			return FLAG_MESSAGE_SERVER_ADD_FACE;
		}

		@Override
		protected void onReadTransmissionData(
				final DataInputStream pDataInputStream) throws IOException {
			this.mID = pDataInputStream.readInt();
			this.mX = pDataInputStream.readFloat();
			this.mY = pDataInputStream.readFloat();
		}

		@Override
		protected void onWriteTransmissionData(
				final DataOutputStream pDataOutputStream) throws IOException {
			pDataOutputStream.writeInt(this.mID);
			pDataOutputStream.writeFloat(this.mX);
			pDataOutputStream.writeFloat(this.mY);
		}

	}

	public static class MoveCarServerMessage extends ServerMessage {
		private int mDirection;

		public MoveCarServerMessage() {

		}

		public MoveCarServerMessage(final int direction) {
			this.mDirection = direction;
		}

		public void set(final int direction) {
			this.mDirection = direction;
		}

		@Override
		public short getFlag() {
			return FLAG_MESSAGE_SERVER_MOVE_CAR;
		}

		@Override
		protected void onReadTransmissionData(DataInputStream pDataInputStream)
				throws IOException {
			this.mDirection = pDataInputStream.readByte();
		}

		@Override
		protected void onWriteTransmissionData(
				DataOutputStream pDataOutputStream) throws IOException {
			pDataOutputStream.writeByte(this.mDirection);
		}

	}

	public static class MoveFaceServerMessage extends ServerMessage {
		private int mID;
		private float mX;
		private float mY;

		public MoveFaceServerMessage() {

		}

		public MoveFaceServerMessage(final int pID, final float pX,
				final float pY) {
			this.mID = pID;
			this.mX = pX;
			this.mY = pY;
		}

		public void set(final int pID, final float pX, final float pY) {
			this.mID = pID;
			this.mX = pX;
			this.mY = pY;
		}

		@Override
		public short getFlag() {
			return FLAG_MESSAGE_SERVER_MOVE_FACE;
		}

		@Override
		protected void onReadTransmissionData(
				final DataInputStream pDataInputStream) throws IOException {
			this.mID = pDataInputStream.readInt();
			this.mX = pDataInputStream.readFloat();
			this.mY = pDataInputStream.readFloat();
		}

		@Override
		protected void onWriteTransmissionData(
				final DataOutputStream pDataOutputStream) throws IOException {
			pDataOutputStream.writeInt(this.mID);
			pDataOutputStream.writeFloat(this.mX);
			pDataOutputStream.writeFloat(this.mY);
		}

	}

	private class ExampleServerConnectorListener implements
			ISocketConnectionServerConnectorListener {
		@Override
		public void onStarted(final ServerConnector<SocketConnection> pConnector) {
			Car.this.toast("CLIENT: Connected to server.");
		}

		@Override
		public void onTerminated(
				final ServerConnector<SocketConnection> pConnector) {
			Car.this.toast("CLIENT: Disconnected from Server...");
			Car.this.finish();
		}

	}

	private class ExampleServerStateListener implements
			ISocketServerListener<SocketConnectionClientConnector> {
		@Override
		public void onStarted(
				final SocketServer<SocketConnectionClientConnector> pSocketServer) {
			Car.this.toast("SERVER: Started.");
		}

		@Override
		public void onTerminated(
				final SocketServer<SocketConnectionClientConnector> pSocketServer) {
			Car.this.toast("SERVER: Terminated.");
		}

		@Override
		public void onException(
				final SocketServer<SocketConnectionClientConnector> pSocketServer,
				final Throwable pThrowable) {
			Debug.e(pThrowable);
			Car.this.toast("SERVER: Exception: " + pThrowable);
		}

	}

	private class ExampleClientConnectorListener implements
			ISocketConnectionClientConnectorListener {
		@Override
		public void onStarted(final ClientConnector<SocketConnection> pConnector) {
			String clientIP = pConnector.getConnection().getSocket()
					.getInetAddress().getHostAddress();

			if (!clientIP.equals(LOCALHOST_IP) && CONNECT_TYPE == SERVER) {
				Car.this.mServerIP = clientIP;
				Car.this.initClient();
			}

			Car.this.toast("SERVER: Client connected: "
					+ pConnector.getConnection().getSocket().getInetAddress()
							.getHostAddress());
		}

		@Override
		public void onTerminated(
				final ClientConnector<SocketConnection> pConnector) {
			Car.this.toast("SERVER: Client disconnected: "
					+ pConnector.getConnection().getSocket().getInetAddress()
							.getHostAddress());
		}

	}
}
